package com.geese.common;

import java.io.IOException;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSONObject;
import com.geese.common.constants.BusinessConstants;
import com.geese.common.util.TemplateMsg;
import com.geese.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.geese.common.util.JSONUtil;
import com.geese.common.util.WebUtil;
import com.geese.module.wechat.core.WeChatConstants;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;

/**
 * Created by yefangyi
 */
public class WXUtil {

    private static final Logger logger = LoggerFactory.getLogger(WXUtil.class);
    private static final String GRANT_TYPE = "client_credential";
    private static final String TYPE = "jsapi";
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    private static final String JS_API_TICKET_URL = "http://api.weixin.qq.com/cgi-bin/ticket/getticket";
    
    private static Cache<String, String> wxParam;
    private static final String ACCESS_TOKEN = "accessToken";
    private static final String JS_API_TICKET = "jsApiTicket";
    
    static {
    	wxParam = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(3600, TimeUnit.SECONDS)
				.build();
    }

    @SuppressWarnings("unchecked")
	private static String getTicket() throws Exception {
    	Map<String, String> param = Maps.newHashMap();
    	param.put("type", TYPE);
    	param.put("access_token", getAccessToken());
    	String response = WebUtil.doGet(JS_API_TICKET_URL, param);
    	Map<String, Object> resObject = JSONUtil.parse(response, Map.class);
    	if(!Objects.equals(String.valueOf(resObject.get("errcode")), "0")) {
    		throw new BusinessException(120007);
    	}
		return String.valueOf(resObject.get("ticket"));
	}
    
    /**
     * 获取jsApiTicket
     */
    public static String getJsApiTicket() {
		try {
			return wxParam.get(JS_API_TICKET, new Callable<String>() {
			    @Override
			    public String call() throws Exception {
			        return getTicket();
			    }
			});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException(120007);
		}
    }

	/**
     * jsApi生成签名
     */
    public static String createSign(Map<String, String> params ,String APP_SECRET) {
    	if(logger.isDebugEnabled()) {
    		logger.debug("createSign : " + JSONUtil.objectToJson(params));
    	}
        try {
        	StringBuffer strb = new StringBuffer();
            List<String> keys = new ArrayList<String>(params.keySet());
            Collections.sort(keys);
            for(int i = 0; i < keys.size(); i++) {
                String key = keys.get(i);
                String value = params.get(key);
            	strb.append((new StringBuilder()).append(i != 0 ? "&" : "").append(key).append("=").append(value).toString());
            }
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(strb.toString().getBytes());
            byte[] digest = md.digest();
            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            String sign =  hexstr.toString();

            if (logger.isDebugEnabled()){
                logger.debug("sign=" + sign);
            }
            return sign;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 发送模板消息
     * @param openId
     * @param data
     * @throws ExecutionException
     * @throws IOException
     */
    public static void send(String openId, Map<String, TemplateMsg.MessageData> data) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + getAccessToken();
        TemplateMsg param = new TemplateMsg();
        param.setTemplate_id(BusinessConstants.TEMPLATE_ID);
        param.setTopcolor("bule");
        param.setUrl(BusinessConstants.TEMPLATE_URL);
        param.setTouser(openId);
        param.setData(data);
        String jsonParam = JSONUtil.objectToJson(param);
        try {
            String result = WebUtil.doPostJSON(url, jsonParam);
            JSONObject json = JSONObject.parseObject(result);
            String code = String.valueOf(json.get("errcode"));
            if (!Objects.equals(code, "0")) {
                logger.warn(String.valueOf("模板消息发送失败----code=" + code + " msg=" + json.get("errmsg")));
                throw new BusinessException(120008);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取accessToken
     * 若不存在缓存，执行回调函数
     * @return
     * @throws ExecutionException
     */
    public static String getAccessToken() {
        try {
			return wxParam.get(ACCESS_TOKEN, new Callable<String>() {
			    @Override
			    public String call() throws Exception {
			        return getToken();
			    }
			});
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException(120006);
		}
    }

    /**
     * 请求微信接口获取accessToken
     * @return
     * @throws IOException 
     */
    @SuppressWarnings("unchecked")
	private static String getToken() throws Exception{
        Map<String, String> param = Maps.newHashMap();
        param.put("grant_type", GRANT_TYPE);
        param.put("appid", WeChatConstants.APP_ID);
        param.put("secret", WeChatConstants.APP_SECRET);
        String response = WebUtil.doGet(ACCESS_TOKEN_URL, param);
        Map<String, Object> resObject = JSONUtil.parse(response, Map.class);
        if(resObject.get("errcode") != null) {
        	logger.error("accessToken response : " + response);
        	throw new BusinessException(120006);
        }
        return String.valueOf(resObject.get("access_token"));
        
    }

    public static void main(String [] args) throws ExecutionException {
        System.out.println(getAccessToken());
    }
}
